﻿using System;
using System.ComponentModel;
using System.Collections.Generic;

namespace Spencen.Mobile.Markup.Extensions
{
    public class Binding : IDynamicValueProvider
    {
        private WeakReference _target;
        private PropertyDescriptor _property;

        public Binding( object target, string propertyName ) : this( target.GetType(), propertyName )
        {
            if ( target == null ) throw new ArgumentNullException( "target" );

            Target = new WeakReference( target );
        }

        public Binding( Type targetType )
        {
            if ( targetType == null ) throw new ArgumentNullException( "targetType" );
            TargetType = targetType;
        }

        public Binding( Type targetType, string propertyName ) : this( targetType )
        {
            if ( string.IsNullOrEmpty( propertyName ) ) throw new ArgumentNullException( "propertyName" );
            
            Path = propertyName;
        }

        public Type TargetType { get; private set; }
        public string Path { get; set; }
        public string StringFormat { get; set; }

        //public PropertyDescriptor Property
        //{
        //    get
        //    {
        //        if ( _property == null )
        //            _property = TypeDescriptor.GetProperties( Target, false ).Find( _propertyName, false );
        //        return _property;
        //    }
        //    set { _property = value; }
        //}

        public object Target
        {
            get { return ( _target == null || !_target.IsAlive ) ? null : _target.Target; }
            set
            {
                _target = new WeakReference( value );
                TargetType = value.GetType();
            }
        }

        public object Value
        {
            get
            {
                var resolvedValue = ResolvePath( Target, Path );

                if ( !string.IsNullOrEmpty( StringFormat ) )
                {
                    var formattableValue = resolvedValue as IFormattable;
                    if ( formattableValue != null )
                    {
                        return formattableValue.ToString( StringFormat, null );
                    }
                }

                return resolvedValue;

                //if ( Target != null && Property != null )
                //    return Property.GetValue( Target );
                //else
                //    return null;
            }
            set
            {
                //if ( Target != null && Property != null )
                //{
                //    if ( Property.IsReadOnly )
                //        throw new InvalidOperationException( string.Format( "Binding to readonly property {0} cannot set value.", Property.Name ) );
                //    Property.SetValue( Target, value );
                //}
            }
        }

        private object ResolvePath( object target, string propertyPath )
        {
            var path = propertyPath.Split( '.' );
            var propertyName = path[ 0 ];
            var propertyIndex = -1;
            if ( propertyName.EndsWith( "]" ) ) // TODO: RegEx
            {
                var startBrace = propertyName.IndexOf( '[' );
                var endBrace = propertyName.IndexOf( ']' );
                propertyName = propertyName.Substring( 0, startBrace );
                propertyIndex = int.Parse( path[ 0 ].Substring( startBrace + 1, endBrace - startBrace - 1 ) );                             
            }

            var property = TypeDescriptor.GetProperties( target, false ).Find( propertyName, false );

            var newTarget = property.GetValue( target );

            if ( propertyIndex >= 0 )
            {
                var collection = newTarget as System.Collections.IList;
                if ( collection == null )
                    throw new InvalidOperationException( string.Format( "Indexer used on non-indexable property {0} of type {1}.", propertyName, property.PropertyType ) );
                newTarget = collection[ propertyIndex ];
            }

            if ( path.Length == 1 )
                return newTarget;
            else
                return ResolvePath( newTarget, string.Join( ".", path, 1, path.Length - 1 ) );
        }

        #region IDynamicValueProvider Members

        public virtual object GetValue( XamlNode context, object parentInstance )
        {
            Target = context.DataContext;
            return Value;
        }

        #endregion
    }
}
